package data.cad;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import data.component.Wall;
import data.component.Station;
import data.component.Room;
import data.component.BoundingBox;
import utils.Geometry;
import org.apache.commons.io.FileUtils;
import org.jetbrains.annotations.NotNull;


/**
 * CAD图纸json依据本次扫描站点的解析结果
 */

public class CAD {
    public String path;

    // 扫描时，所处施工阶段
    public String stage;

    // 站点规划算法给出的当前站点能扫到的所有墙
    public List<Wall> walls = new ArrayList<Wall>();

    // 站点理应实际扫到的墙
    public List<Wall> filteredWalls = new ArrayList<Wall>();

    // 本次扫描的站点的信息
    public Station station;

    // 站点所处的房间信息
    public Room room;

    // 这张图纸的boundingBox信息, 包含结构图与建筑图的差距
    public BoundingBox boundingBox;

    public CAD(String drawingPath, @NotNull String stage, String stationId) {
        this.path = drawingPath;
        this.stage = stage.equals("concrete") ? "Structure" : "Architecture";
        File file = new File(this.path);
        String fileString = null;
        try {
            fileString = FileUtils.readFileToString(file, "utf-8");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        var cadJson = JSON.parseObject(fileString);
        InitStation(cadJson, stationId);

        InitWalls(cadJson);

        InitRoom(cadJson);

        InitFilteredWalls();

        InitBoundingBox();

    }


    /**
     * parse station information from the CAD.json
     *
     * @param cadJson   图纸json对象
     * @param stationId 站点id
     */
    private void InitStation(@NotNull JSONObject cadJson, String stationId) {
        var a = cadJson.get("scanstation");

        var jStations = ((JSONArray) cadJson.get("scanstation"));
        var jStationQ = jStations.stream().filter(s -> ((JSONObject) s).get("id").toString().equals(stationId));
        var jStation = ((JSONObject) jStationQ.toArray()[0]);

        var stageList = jStation.getObject("stage", String[].class);
        var stationStage = Arrays.asList(stageList).contains("concrete") ? "Structure" : "Architecture";

        var mainWallIds = Arrays.asList(jStation.getObject("mainwalls", String[].class));
        List<String> dryWallIds = null;
        String roomId = null;
        if (stationStage.equals("Architecture")) {
            dryWallIds = Arrays.asList(jStation.getObject("drywalls", String[].class));
            roomId = jStation.get("room").toString();
        }

        var location = jStation.getObject("point", Double[].class);
        this.station = new Station(stationId, roomId, stationStage, mainWallIds, dryWallIds, location);

    }

    /**
     * parse walls that the station can scan from the CAD.json
     *
     * @param cadJson 图纸json对象
     */
    private void InitWalls(@NotNull JSONObject cadJson) {
        // 获取墙对应的线
        var totalWallIds = new ArrayList<String>(station.mainWallIds);
        totalWallIds.addAll(station.dryWallIds);

        var jLines = ((JSONArray) cadJson.get("line")).stream().filter((line -> totalWallIds.contains(((JSONObject) line).get("id").toString()))).toArray();

        // 获取线的所有端点
        var linePointIds = new ArrayList<String>();
        for (var jLine : jLines) {
            linePointIds.add(((JSONObject) jLine).getObject("start", String.class));
            linePointIds.add(((JSONObject) jLine).getObject("end", String.class));
        }

        // 过滤点
        var jPoints = ((JSONArray) cadJson.get("point")).stream().filter(point -> linePointIds.contains(((JSONObject) point).get("id").toString())).toArray();

        // 获取点id坐标映射
        var pointMap = new HashMap<String, Double[]>();

        for (var jPoint : jPoints) {
            var location = ((JSONObject) jPoint).getObject("location", Double[].class);
            var pointId = ((JSONObject) jPoint).get("id").toString();
            pointMap.put(pointId, location);
        }

        // 获取墙对象
        for (var jLine : jLines) {
            var l = ((JSONObject) jLine);
            if (l.get("start").toString().equals(l.get("end").toString()))
                continue;
            var start = pointMap.get(l.get("start").toString());
            var end = pointMap.get(l.get("end").toString());

            var wallType = station.mainWallIds.contains(l.get("id").toString()) ? "MainWall" : "DryWall";
            var wall = new Wall(l.get("id").toString(), start, end, wallType);
//            wall.GetScanQuality(this.station.location);
            this.walls.add(wall);
        }
    }

    /**
     * parse the room which the station in from the CAD.json
     *
     * @param cadJson 图纸json对象
     */

    private void InitRoom(JSONObject cadJson) {
        // 获取房间
        var jRoom = ((JSONObject) ((JSONArray) cadJson.get("room")).stream().filter(r -> ((JSONObject) r).get("id").toString().equals(station.roomId)).toArray()[0]);
        var contours = new ArrayList<>(Arrays.asList(jRoom.getObject("points", Double[][].class))) ;

        this.room = new Room(station.roomId, contours, jRoom.get("name").toString(), jRoom.get("apartment").toString());
    }

    /**
     * filter vertical walls by wrapper layer's number of the scan station
     */
    private void InitFilteredWalls()
    {
        // 找出竖直墙

        var verticalWalls = walls.stream()
                .filter(w -> w.length > 500 && (w.start[0].intValue() / 10 == (w.end[0].intValue() / 10)))
                .collect(Collectors.toList());
        if (verticalWalls.size() == 0) {
            this.filteredWalls = this.walls;
            return;
        }

        // 找出站点第一层包裹墙面的距离
        var xDistance = verticalWalls.stream()
                .filter(w -> w.length > 1000)
                .map(w -> Math.abs(w.length - station.location[0]))
                .collect(Collectors.toList());
        if(xDistance.size() == 0)
        {
            this.filteredWalls = this.walls;
            return;
        }
        var minXDistance = Collections.min(xDistance);

        for(var w : this.walls)
        {
            // 与站点距离比最近横向距离还多3米的墙 滤去
            if (verticalWalls.contains(w) && Math.abs(w.start[0] - this.station.location[0]) - minXDistance > 3000)
                continue;

            var distance = Geometry.CalcDistance(w.center, this.station.location);

            // 距离5米内或足够长的保留
            if (distance < 5000 || w.length / distance > 0.1)
                this.filteredWalls.add(w);
        }

    }

    private void InitBoundingBox()
    {
        var points = new ArrayList<Double[]>();
        for(var w : filteredWalls)
        {
            points.add(w.start);
            points.add(w.end);
        }

        this.boundingBox = new BoundingBox(points);
    }

    public static List<Wall> GetMappingWallsByXmlWallId(List<Wall> cadWalls,HashMap<String, Wall> map, String xmlWallId)
    {
        var walls = new ArrayList<Wall>();
        for (var wallId : map.keySet())
        {
            if (map.get(wallId) == null)
                continue;
            if (Integer.parseInt(map.get(wallId).id) / 256 == Integer.parseInt(xmlWallId))
            {
                walls.add(cadWalls.stream().filter(wall -> wall.id.equals(wallId)).collect(Collectors.toList()).get(0));
            }
        }
        return walls;
    }

    public static Wall GetLongestMappingWallByXmlWallId(List<Wall> cadWalls,HashMap<String, Wall> map, String xmlWallId)
    {
        var walls = GetMappingWallsByXmlWallId(cadWalls, map, xmlWallId);
        if (walls.isEmpty())
            return null;
        return walls.stream().max(Comparator.comparing(Wall::GetLength)).get();
    }
}
